"""
媒体播放器组件。
"""
import typing
from typing import ClassVar
from .recorder import MediaRecorder
from ..base import BaseSession, BaseSessionType, BaseSessionParentType
from ..framework import dispatch, session_received
from ..utils import callback, FloatValue, to_any
from ..widget import WidgetFileUploader


@session_received
class MediaPlayer(BaseSession):
    """
    媒体播放器。
    可以实现在设备端播放网络音乐。
    """
    name: ClassVar = 'MediaPlayer'

    @classmethod
    async def create(cls: typing.Type[BaseSessionType], parent: BaseSessionParentType, *args: typing.Any,
                     **kwargs: typing.Dict) -> BaseSessionType:
        return await super().create(parent, *args, **kwargs)

    async def on_receive(self, msg):
        """
        组件的事件转发。
        """
        await super().on_receive(msg)

        sid, fid = msg.session, msg.future
        match msg.name:
            case 'onLoad':
                dispatch(self.on_load(*msg.argv, session_id=sid, futureId=fid))
            case 'onLoadFromWidgetFileUploader':
                dispatch(self.on_load_from_widget_from_uploader(*msg.argv, session_id=sid, futureId=fid))
            case 'onLoadFromMediaRecorder':
                dispatch(self.on_load_from_media_recorder(*msg.argv, session_id=sid, futureId=fid))
            case 'onStop':
                dispatch(self.on_stop(*msg.argv, session_id=sid, futureId=fid))
            case 'onIsPlaying':
                dispatch(self.on_is_playing(*msg.argv, session_id=sid, future_id=fid))
            case 'onDuration':
                dispatch(self.on_duration(*msg.argv, session_id=sid, future_id=fid))
            case 'onCurrentPosition':
                dispatch(self.on_current_position(*msg.argv, session_id=sid, future_id=fid))
            case 'onIsLooping':
                dispatch(self.on_is_looping(*msg.argv, session_id=sid, future_id=fid))

    @callback(str)
    async def on_load(self, url, **kwargs):
        """
        当音乐被加载后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        session_id = kwargs['session_id'] if 'session_id' in kwargs else None
        if not session_id:
            return
        future_id = kwargs['futureId'] if 'futureId' in kwargs else 0
        self.set_action_result(session_id, future_id, url)

    @callback(bool)
    async def on_load_from_widget_from_uploader(self, result, **kwargs):
        """
        当音乐从文件上传器小部件被加载后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        session_id = kwargs['session_id'] if 'session_id' in kwargs else None
        if not session_id:
            return
        future_id = kwargs['futureId'] if 'futureId' in kwargs else 0
        self.set_action_result(session_id, future_id, result)

    @callback(bool)
    async def on_load_from_media_recorder(self, result, **kwargs):
        """
        当音乐从录音机组件被加载后，此方法被自动调用。
        通常不需要关注此方法的实现。
        """
        session_id = kwargs['session_id'] if 'session_id' in kwargs else None
        if not session_id:
            return
        future_id = kwargs['futureId'] if 'futureId' in kwargs else 0
        self.set_action_result(session_id, future_id, result)

    async def load(self, url: str):
        """
        从url加载资源。
        :param url: 音乐的网络地址。
        调用示例：
        await player.load('https://example.com/test.mp3')
        调用此方法后不会立即播放，需要后续调用play方法才能播放。
        """
        return await self.send_action_until_return('load', to_any(url))

    async def load_from_widget_file_uploader(self, uploader: WidgetFileUploader):
        """
        从文件上传器加载资源。
        :param uploader: 文件上传器小部件。
        调用示例：
        await player.load_from_widget_file_uploader(uploader)
        调用此方法后不会立即播放，需要后续调用play方法才能播放。
        """
        return await self.send_action_until_return('loadFromWidgetFileUploader', uploader.reference)

    async def load_from_media_recorder(self, recorder: MediaRecorder):
        """
        从媒体录音机加载资源。
        :param recorder: 媒体录音机组件。
        调用示例：
        await player.load_from_media_recorder(recorder)
        调用此方法后不会立即播放，需要后续调用play方法才能播放。
        """
        return await self.send_action_until_return('loadFromMediaRecorder', recorder.reference)

    @callback(bool)
    async def on_stop(self, res, **kwargs):
        """
        如果音乐播放停止将自动调用此方法。
        通常不需要关注此方法的实现。
        """
        session_id = kwargs['session_id'] if 'session_id' in kwargs else None
        if not session_id:
            return
        future_id = kwargs['futureId'] if 'futureId' in kwargs else 0
        self.set_action_result(session_id, future_id, res)

    async def stop(self):
        """
        停止播放。
        """
        return await self.send_action_until_return('stop')

    async def pause(self):
        """
        暂停播放。
        """
        dispatch(self.send_action('pause'))

    async def play(self):
        """
        开始播放。
        注意： android（android-ktx）平台播放器对某些格式的音频播放具有BUG，例如opus格式在播放后需要重新调用load方法，否则将无法重新播放。
        """
        dispatch(self.send_action('play'))

    @callback(bool)
    async def on_is_playing(self, is_playing: bool, session_id: int, future_id: int):
        """
        当获取播放状态成功后自动调用此方法。
        通常不需要关注此方法的实现。
        """
        self.set_action_result(session_id, future_id, is_playing)

    async def is_playing(self) -> bool:
        """
        判断是否正在播放。
        """
        return await self.send_action_until_return('isPlaying')

    @callback(int)
    async def on_duration(self, num: int, session_id: int, future_id: int):
        """
        当获取播放总时长成功后自动调用此方法。
        通常不需要关注此方法的实现。
        """
        self.set_action_result(session_id, future_id, num)

    async def duration(self) -> int:
        """
        获取当前媒体的总时长。
        """
        return await self.send_action_until_return('duration')

    @callback(int)
    async def on_current_position(self, num: int, session_id: int, future_id: int):
        """
        当获取当前播放位置成功自动调用此方法。
        通常不需要关注此方法的实现。
        """
        self.set_action_result(session_id, future_id, num)

    async def current_position(self) -> int:
        """
        获取当前播放位置。
        """
        return await self.send_action_until_return('currentPosition')

    @callback(bool)
    async def on_is_looping(self, is_looping: bool, session_id: int, future_id: int):
        """
        当获取循环播放状态成功自动调用此方法。
        通常不需要关注此方法的实现。
        """
        self.set_action_result(session_id, future_id, is_looping)

    async def is_looping(self) -> bool:
        """
        判断是否开启了循环播放。
        """
        return await self.send_action_until_return('isLooping')

    async def set_on_completion_listener(self, func: typing.Optional[typing.Callable] = None):
        """
        设置播放完成的监听函数，如果传入None则不监听。
        调用示例：
        async def cb(): pass
        await player.set_on_completion_listener(cb)
        await player.set_on_completion_listener(None)
        """
        dispatch(self.send_action('setOnCompletionListener', self.create_callback(func)))

    async def set_looping(self, enabled: bool):
        """
        设置循环播放。
        """
        dispatch(self.send_action('setLooping', to_any(enabled)))

    async def set_speed(self, rate: float):
        """
        设置播放速度。
        """
        dispatch(self.send_action('setSpeed', to_any(rate, FloatValue)))

    async def set_volume(self, left: float, right: typing.Optional[float] = None):
        """
        设置播放音量，值在0.0到1.0之间。
        如果传入一个参数，则同时控制两个声道；
        如果传入两个参数，则分别控制两个声道。
        """
        vol = right if right is not None else left
        dispatch(self.send_action('setVolume', to_any(left, FloatValue), to_any(vol, FloatValue)))

    async def seek_to(self, num: int):
        """
        移动播放光标，快进， 快退
        参数是移动到的播放位置
        """
        dispatch(self.send_action('seekTo', to_any(num)))
